package com.pinoc.demo.randomlottery;

import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author pinoc
 * @date 2023/11/13
 */
public class Lottery2 {
	public static void main(String[] args) {
		final Map<String, Integer> awardStockMap = new ConcurrentHashMap<>();
		awardStockMap.put("1", 3000);
		awardStockMap.put("2", 2000);
		awardStockMap.put("3", 1500);
		awardStockMap.put("5", 1000);
		awardStockMap.put("10", 100);
		awardStockMap.put("20", 10);
		awardStockMap.put("50", 5);
		awardStockMap.put("100", 2);
		// 无自定义中奖概率时，权重默认等于库存
		final Map<String, Integer> awardWeightMap = new ConcurrentHashMap<>(awardStockMap);
		final Map<String, Integer> initAwardStockMap = new ConcurrentHashMap<>(awardStockMap);
		// 理论可以抽完所有奖品所需抽奖次数 = 奖品数 × 中奖概率倒数 = 7617 * 100/15
		final long drawNum = awardStockMap.values().stream().collect(Collectors.summarizingInt(i -> i)).getSum() * 100/15;
		// 自定义 中奖概率 15%
		final int customThreshold = 15;
		// 每天实际中奖计数
		Map<String, Integer> dailyWinCountMap = new ConcurrentHashMap<>(16);
		// 模拟每次抽奖
		for (int j = 0; j < drawNum; j++) {
			// 确定是否中奖
			int randNum = new Random().nextInt(100);
			if (randNum > customThreshold) {
				dailyWinCountMap.compute("未中奖", (k, v) -> v == null ? 1 : v + 1);
				// 未中奖
				continue;
			}
			// 中奖：确定是哪个奖品
			// 排除掉库存为0的奖品
			Map<String, Integer> awardWeightHaveStockMap = awardWeightMap.entrySet().stream().filter(e -> awardStockMap.get(e.getKey()) > 0)
					.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
			// 奖池已为空
			if (awardWeightHaveStockMap.isEmpty()) {
				System.out.printf("第%d次抽奖 奖品已被抽完%n", j);
				break;
			}
			int totalWeight = (int) awardWeightHaveStockMap.values().stream().collect(Collectors.summarizingInt(i -> i)).getSum();
			randNum = new Random().nextInt(totalWeight);
			int prev = 0;
			String choosedAward = null;
			for (Map.Entry<String, Integer> e : awardWeightHaveStockMap.entrySet()) {
				if (randNum >= prev && randNum < prev + e.getValue()) {
					// 落入此区间 中奖
					choosedAward = e.getKey();
					dailyWinCountMap.compute(choosedAward, (k, v) -> v == null ? 1 : v + 1);
					break;
				}
				prev = prev + e.getValue();
			}
			// 减小库存
			awardStockMap.compute(choosedAward, (k, v) -> v - 1);
		}
		// 每日各奖品中奖计数
		System.out.println("每日各奖品中奖计数: ");
		dailyWinCountMap.entrySet().stream().sorted((e1, e2) -> e2.getValue() - e1.getValue()).forEach(System.out::println);
		awardStockMap.forEach((k, v) -> {
			if (v > 0) {
				System.out.printf("奖品：%s, 总库存： %d, 剩余库存： %d%n", k, initAwardStockMap.get(k), v);
			}
		});
	}
}
